import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:track_demo/main.dart';

// PV埋点
class RouterTrackObserver extends NavigatorObserver {
  final Function(String) pvEventTrancking;

  RouterTrackObserver({this.pvEventTrancking});

  @override
  void didPush(Route route, Route previousRoute) {
    isPush = true;
    routeContext = route.navigator.context;
  }

  @override
  void didPop(Route route, Route previousRoute) {
    isPush = false;
    routeContext = previousRoute.navigator.context;
    if (pathList.length != 0) {
      print("页面POP：" + pathList.last);
      pathList.removeLast();
    }
  }
}

class TYTrack extends StatelessWidget {
  Widget child;
  Function(String path) tapCallback;
  final Map<Type, GestureRecognizerFactory> gestures =
      <Type, GestureRecognizerFactory>{};

  TYTrack({this.child, this.tapCallback});

  @override
  Widget build(BuildContext context) {
    return RawGestureDetector(gestures: <Type, GestureRecognizerFactory>{
      TYTapGestureRecognizer:
          GestureRecognizerFactoryWithHandlers<TYTapGestureRecognizer>(
        () => TYTapGestureRecognizer(context, callback: tapCallback),
        (TYTapGestureRecognizer instance) {
          instance
            ..onTapDown = (details) {
              instance.tapDownDetails = details;
            }
            ..onTapUp = (details) {
              instance.tapUpDetails = details;
            };
        },
      ),
    }, child: child);
  }
}

class TYTapGestureRecognizer extends TapGestureRecognizer {
  TapUpDetails tapUpDetails;
  TapDownDetails tapDownDetails;
  BuildContext context;
  Function(String path) callback;
  Element _hitElement; // 被点击的element
  String path;
  final String joinCode = "->";

  //FAQ 属性
  // 解决重叠手势问题
  int parentLevel; // 父节点层级
  int childrenLevel; // 子节点层级
  // 解决标题栏和列表重叠问题
  bool isVisitAppBar; // 是否正在遍历标题栏
  bool isAppBarHit; // 标题栏child是否被命中
  // 下标唯一性问题
  bool isCurrentHited; // child列表中的当前element的子child是否被命中
  int hitItemIndex; // 命中的item在列表中的下标

  TYTapGestureRecognizer(this.context, {this.callback});

  @override
  void rejectGesture(int pointer) {
    // super.rejectGesture(pointer);
    // 开启手势
    try {
      super.acceptGesture(pointer);

      if (null != tapUpDetails) {
        // 查找child，找到触发element
        initialData();
        context.visitChildElements(_visitor);  // 找到触发的element
        goThroughAncestors(); // 遍历祖先，确定页面 Scaffold
        goThroughChildren(); // 遍历子节点，确定页面内唯一性

        var listItemRes = "标记位：$hitItemIndex ";
        callback("父层级：$parentLevel 子层级：$childrenLevel $listItemRes" + path);

        clear();
      }
    } catch (e) {
      print("手势检测错误： " + e.toString());
    }
  }

  initialData() {
    path = ""; // 路径

    parentLevel = 0; // 父节点层级
    childrenLevel = 0; // 子节点层级

    isVisitAppBar = false; // 是否正在遍历标题栏
    isAppBarHit = false; // 标题栏child是否被命中

    isCurrentHited = false;
    hitItemIndex = -1;
  }

  clear() {
    tapUpDetails = null;
    tapDownDetails = null;
    _hitElement = null;
  }

  void _visitor(Element element) {
    if (null != element && element.renderObject is RenderBox) {
      if (element.widget is AppBar) {
        isVisitAppBar = true;
      }

      if (isAppBarHit && !isVisitAppBar) return; // 标题栏被命中，且标题栏child遍历完成后，取消遍历

      // 首先是要判断是手势部件，筛选掉其他所有的widget
      if (element.widget is Listener) {
        RenderBox box = element.renderObject;
        Offset origin = box.localToGlobal(Offset.zero);
        Rect rect = Rect.fromLTWH(
            origin.dx, origin.dy, box.size.width, box.size.height);

        // 点击位在手势部件范围内
        // Offset
        var upGlobalPosition = tapUpDetails.globalPosition;
        var downGlobalPosition = tapUpDetails.globalPosition;
        if (rect.contains(upGlobalPosition) &&
            rect.contains(downGlobalPosition)) {
          _hitElement = element; //在这里拿到触发的element
          isCurrentHited = true;
          if (isVisitAppBar) {
            isAppBarHit = true;
          }
        }
      }
      //这里循环遍历找到最里面的那个手势部件，它就是触发的那个手势部件
      if (element is MultiChildRenderObjectElement) {
        MultiChildRenderObjectElement ele = element;
        List childElements = ele.children.toList();
        for (int i = 0; i < childElements.length; i++) {
          childElements[i].visitChildElements(_visitor);
          if (isCurrentHited) {
            hitItemIndex = i;
            isCurrentHited = false;
          }
        }
      } else {
        element.visitChildren(_visitor);
      }
      if (element.widget is AppBar) {
        isVisitAppBar = false;
      }
    }
  }

  // 遍历祖先节点，区分页面
  void goThroughAncestors() {
    bool upIsPageWidget = false; // 上一层是否是页面节点

    _ancestorVisitor(Element element) {
      parentLevel++;
      if (null != element) {
        if ((element.widget is StatelessWidget ||
                element.widget is StatefulWidget) &&
            !element.widget.runtimeType.toString().startsWith("_")) {
          if (upIsPageWidget) {
            path = element.widget.runtimeType.toString() + joinCode + path;
            if (element.widget is CupertinoApp) {
              return true;
            }
            return false;
          }
          if (element.widget is StatelessWidget ||
              element.widget is StatefulWidget) {
            if (element.renderObject is RenderBox) {
              path = element.widget.runtimeType.toString() + joinCode + path;
            }
            if (element.widget is Scaffold) {
              upIsPageWidget = true;
            }
          }
        }
      }
      return true;
    }

    if (null != _hitElement) {
      _hitElement.visitAncestorElements(_ancestorVisitor);
    }
  }

  // 遍历子节点，确定唯一性
  void goThroughChildren() {
    _childrenVisitor(Element element) {
      childrenLevel++;
      bool shouldStop = false;
      if (null != element) {
        if (element.widget is StatelessWidget ||
            element.widget is StatefulWidget) {
          if (element.widget is Text) {
            Text mWidget = element.widget;
            path = path +
                joinCode +
                element.widget.runtimeType.toString() +
                mWidget.data.toString();
            shouldStop = true;
          } else if (element.widget is Image) {
            Image mWidget = element.widget;
            path = path +
                joinCode +
                element.widget.runtimeType.toString() +
                mWidget.image.toString();
            shouldStop = true;
          } else {
            path = path + joinCode + element.widget.runtimeType.toString();
          }
        }
      }
      if (!shouldStop) {
        element.visitChildElements(_childrenVisitor);
      }
    }

    if (null != _hitElement) {
      _hitElement.visitChildElements(_childrenVisitor);
    }
  }
}
